Function.RegisterNamespace("xUnit.js.Console.Output.OutputStrategy");

xUnit.js.Console.Output.OutputStrategy.Json=function(){
    // Private Members
    var _errors;
    var _output;

    // IOutputStrategy Members
    this.OutputLevel="";

    this.Prologue=function(){
        _errors=[];
        _output={
            files:[],
            facts:[],
            run:{
                count:0,
                errorCount:0,
                failureCount:0,
                warningCount:0,
                skippedCount:0,
                duration:0,
                timestamp:0            
            }
        };
    };

    this.Epilogue=function(){
        System.Environment.Write(new System.Script.ObjectSerializer().Serialize(_output));
    };

    this.BeginFileLoad=function(){
        _output.files.length=0;
    };

    this.FileLoadSuccess=function(file,duration){
    };

    this.CompleteFileLoad=function(files,duration){
        Array.ForEach(files,listFile);
        //_output.files.duration=duration/1000;
    };

    this.BeginRun=function(){
        _output.facts.length=0;
        _output.run.count=_output.run.errorCount=_output.run.failureCount=_output.run.skippedCount=_output.run.warningCount=_output.run.duration=0;
        _output.run.timestamp=new Date();
        delete _output.run.errors;
        delete _output.run.failures;
        delete _output.run.warnings;
        delete _output.run.skipped;
    };

    this.CompleteRun=function(successes,failures,errors,warnings,skipped,duration){
        var count=successes.length+failures.length+errors.length+warnings.length+skipped.length;
        _output.run.count=count;
        _output.run.failureCount=failures.length;
        _output.run.errorCount=errors.length+_errors.length;
        _output.run.warningCount=warnings.length;
        _output.run.skippedCount=skipped.length;
        _output.run.duration=duration/1000;
        if(failures.length){
            _output.run.failures=[];
            Array.ForEach(failures,listFailure);
        }
        if(errors.length){
            _output.run.errors=[];
            Array.ForEach(errors,listError);
        }
        if(_errors.length){
            if(!_output.run.errors){
                _output.run.errors=_errors;
            }else{
                _output.run.errors=_output.run.errors.concat(_errors);
            }
        }
        if(warnings.length){
            _output.run.warnings=[];
            Array.ForEach(warnings,listWarning);
        }
        if(skipped.length){
            _output.run.skipped=[];
            Array.ForEach(skipped,listSkipped);
        }
    };
    
    this.BeginComponent=function(component){
        // no-op
    };

    this.CompleteComponent=function(component,duration){
        if(!Object.IsType(xUnit.js.Model.Fact,component))return;
        _output.facts.push({
            path:getPath(component),
            result:getResult(component.State.Result),
            message:component.State.Message||'',
            duration:duration/1000
        });
    };

    this.Enumerate=function(component){
        _output.facts.push({
            path:getPath(component)
        });
    };

    this.Error=function(error){
        _errors.push({message:formatError(error)});
    }
    
    // IStrategySpecification Members
    this.IsSatisfiedBy=function(candidate){
        return String.Equals(xUnit.js.Console.Output.OutputFormatter.OutputTypes.json,candidate);
    };
    
    // Private Methods
    function formatError(error){
        return error&&error.toString(isVerbose())||"[No Message]";
    }
    
    function getPath(fact){
        return fact.GetPath().split('.').slice(1).join('.');
    }
    
    function getResult(result){
        switch(result){
            case xUnit.js.Model.Result.Error:
                return "Error";
            case xUnit.js.Model.Result.Failure:
                return "Failure";
            case xUnit.js.Model.Result.Skipped:
                return "Skipped";
            case xUnit.js.Model.Result.Success:
                return "Success";
            case xUnit.js.Model.Result.Warning:
                return "Warning";
            default:
                return "???";
        }
        return '';
    }

    function isVerbose(){
        return this.OutputLevel==xUnit.js.Console.Output.OutputFormatter.OutputLevels.verbose;
    }
    isVerbose=Function.GetDelegate(isVerbose,this);
    
    function listError(run,context){
        listMessage(run,context,_output.run.errors);
    }

    function listFailure(run,context){
        listMessage(run,context,_output.run.failures);
    }

    function listWarning(run,context){
        listMessage(run,context,_output.run.warnings);
    }

    function listFile(file,context){
        _output.files.push({
            path:file
        });
    }

    function listSkipped(run,context){
        _output.run.skipped.push({
            path:getPath(run.Component),
            message:run.State.Message||"[no message]"
        });
    }    

    function listMessage(run,context,collection){
        collection.push({
            "file":run.Component.File,
            index:run.State.Index,
            path:getPath(run.Component),
            type:run.State.Message&&run.State.Message.name||"Exception",
            message:formatError(run.State.Message)
        });
    }
};

xUnit.js.Console.Output.OutputStrategy.Json.Implement(xUnit.js.Console.Output.IOutputStrategy,'xUnit.js.Console.Output.OutputStrategy.Json');
xUnit.js.Console.Output.OutputStrategy.Json.Implement(System.Script.Strategy.IStrategySpecification,'xUnit.js.Console.Output.OutputStrategy.Json');